Skip to main content

06 Inheritance

继承

在继承时,衍生类会继承基类的所有成员。构造函数不属于成员,不会被继承,但可以被衍生类调用。

所有类都是 Object 的子类。

衍生类会继承基类的 publicprotected 成员。如果两个类位于一个包中,衍生类也会继承package-private成员。由于嵌套类可以访问包装类的所有成员,因此,如果某个基类包含一个继承了它的嵌套类,则这个衍生类可以间接地访问基类的私有成员。

对象可以显示地“类型转换”。在下面的例子中, obj 事实上是一个 MountainBike 对象,但是编译器只将其识别为 Object 对象,直接赋值会报错。通过下列语法,我们告诉编译器 objMountainBike 对象。

Object obj = new MountainBike();
MountainBike myBike = (MountainBike)obj;

Java不允许一个类继承多个类,但允许实现多个接口。

覆盖和隐藏方法

在衍生类可以定义和基类签名相同的实例方法,这种情况下基类的方法会被覆盖。可以使用 @Override 注解告知编译器要覆盖某个方法。如果定义和基类签名相同的静态方法,则基类的方法被隐藏。

覆盖的方法可以修改访问权,但是必须大于等于被覆盖的方法。

默认方法指方法在接口中实现而不是类中实现。一个类可以继承多个接口和一个类,则它们定义的方法可能产生冲突,此时编译器会选择衍生类的方法。

优先级最高的为基类的实例方法。

public class Horse {
public String identifyMyself() {
return "I am a horse.";
}
}

public interface Flyer {
default public String identifyMyself() {
return "I am able to fly.";
}
}

public interface Mythical {
default public String identifyMyself() {
return "I am a mythical creature.";
}
}

public class Pegasus extends Horse implements Flyer, Mythical {
public static void main(String... args) {
Pegasus myApp = new Pegasus();
System.out.println(myApp.identifyMyself());
// I am a horse.
}
}

已经被覆盖的方法会被忽略。

public interface Animal {
default public String identifyMyself() {
return "I am an animal.";
}
}

public interface EggLayer extends Animal {
default public String identifyMyself() {
return "I am able to lay eggs.";
}
}

public interface FireBreather extends Animal { }

public class Dragon implements EggLayer, FireBreather {
public static void main (String... args) {
Dragon myApp = new Dragon();
System.out.println(myApp.identifyMyself());
// I am able to lay eggs.
}
}

在其它情况下会报错。如果要继承两个冲突的、独立的接口,则必须在衍生类中覆盖相关方法。

接口中的静态方法不会被继承。

多态性

在衍生类中,定义与基类中名字相同的字段(即使类型不同)会隐藏基类的字段。不推荐这种方式。

通过 super 关键字可以调用基类被覆盖的方法等。

super.printMethod();
super(startCadence, startSpeed, startGear); // constructor

如果衍生类的构造函数没有调用基类的构造函数,则Java会在衍生类构造函数的最前面插入对基类无参构造函数的调用。如果基类没有无参构造函数,就会导致报错。

可以用 final 关键字标记方法,被标记的方法不能被重载。如果用 final 关键字标记类,则这个类不能有衍生类。

Object 类

这部分介绍了一些方法。

String toString();
boolean equals();

覆盖这个方法后,可以使用==运算符。如果覆盖 equals() ,必须覆盖 hashCode()

int hashCode()

这个函数返回由哈希算法生成的对象值。如果两个对象相等,则哈希值必须相等。

getClass()

这个方法不能被覆盖。

如果某个类实现了 Cloneable 接口,则可以使用 clone() 函数。简单地在类后面添加 implements Cloneable 即可。如果类中包含引用,则可能需要重载 clone 函数。重载时只能选择下面两种之一的写法。

public Object clone() throws CloneNotSupportedException
protected Object clone() throws CloneNotSupportedException

finalize() 方法用于回收,但是由于是否被调用和调用时机不确定等原因,不建议重载这个方法。

抽象方法和抽象类

abstract 关键字标记的方法和类称为抽象方法和抽象类。抽象方法即声明了但没有实现的方法。含有或可能含有抽象方法的类称为抽象类。抽象类不能被实例化,但是可以被继承。

在接口中,非默认方法事实上是隐式抽象的,所以没有必要用 abstract 关键字标记接口。

抽象类和接口在许多地方相似,但也有不同。例如,接口的所有默认函数都为 public ,所有字段都是 public static final 。如果需要其它类型的字段,就不能使用接口而要使用抽象类。